﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace MedicWave.Visualization3d
{



    /// <summary>
    /// A control with 3d plot view and some settings
    /// </summary>
    public partial class WidgetPlot3D : UserControl
    {
        /// <summary>
        /// A copy of the entire plot data. Subsets are computed from this.
        /// </summary>
        InternalPlotData m_PlotData;



        public WidgetPlot3D()
        {
            InitializeComponent();
            this.Focus();
            this.MouseWheel += new MouseEventHandler(WidgetPlot3D_MouseWheel);
            plotTypeCombo.MouseWheel += new MouseEventHandler(plotTypeCombo_MouseWheel);
        }

        #region External api

        /// <summary>
        /// Sets data to plot, using default settings.
        /// </summary>
        /// <param name="plotData">PlotData object containing data to plot.</param>
        public void SetData(PlotData plotData)
        {
            dataZoomScrollBar.Value = 0;
            InternalPlotData internalPlotData = InternalPlotData.FromRawData(plotData);
            plotTypeCombo.SelectedItem = internalPlotData.PlotSettings.PlotType;
            m_PlotData = internalPlotData;
            //reset zoom



            //display loading message/lock plot
            plot3DView.IsLoading = true;

            //compute data subset-in this case, make a copy
            InternalPlotData plotSubset = m_PlotData.GetDataSubset();
            plot3DView.SetData(plotSubset);


            plot3DView.Focus();

        }

        /// <summary>
        /// Sets data to plot using specified settings.
        /// </summary>
        /// <param name="plotData">PlotData object containing data to plot.</param>
        /// <param name="plotSettings">Plot settings.</param>
        public void SetData(PlotData plotData, PlotSettings plotSettings)
        {

            dataZoomScrollBar.Value = 0;
            InternalPlotData internalPlotData = InternalPlotData.FromRawData(plotData);
            internalPlotData.PlotSettings.Update(plotSettings);

            if (plotSettings.LockPlotType)
                plotTypeCombo.Enabled = false;

            m_PlotData = internalPlotData;

            //display loading message/lock plot
            plot3DView.IsLoading = true;
            plotTypeCombo.SelectedItem = internalPlotData.PlotSettings.PlotType;
            //compute data subset
            InternalPlotData plotSubset = m_PlotData.GetDataSubset();
            plot3DView.SetData(plotSubset);
            plot3DView.Focus();
        }

        /// <summary>
        /// Sets new settings for current plot. If there's a change in filtering or data range this can take
        /// a few seconds, depending on data size.
        /// </summary>
        /// <param name="newSettings">New settings</param>
        public void SetPlotSettings(PlotSettings newSettings)
        {
            ///no data to plot
            if (m_PlotData == null) return;

            if (newSettings.LockPlotType)
                plotTypeCombo.Enabled = false;

            //reset zoom
            dataZoomScrollBar.Value = 0;
            //invalid data range
            if (newSettings.MinRT >= newSettings.MaxRT || newSettings.MinMZ >= newSettings.MaxMZ)
            {
                //invalid subset, use current
                newSettings.MinRT = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinRetentionTime;
                newSettings.MaxRT = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxRetentionTime;
                newSettings.MinMZ = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinMZ;
                newSettings.MaxMZ = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxMZ;
            }


            //invalid filtering
            if (newSettings.FilteringAmount <= 0 || newSettings.FilteringAmount > 100)
            {
                //invalid filtering, use current
                newSettings.FilteringAmount = m_PlotData.PlotSettings.PlotSubsetDescription.m_fIntensityTreshold;
            }
            InternalPlotSettings newPlotSettings = (InternalPlotSettings)m_PlotData.PlotSettings.Clone();

            //update internal settings
            newPlotSettings.Update(newSettings);

            //display loading message/lock plot
            plot3DView.IsLoading = true;

            //did subset/filtering change?
            if (newPlotSettings.PlotSubsetDescription.m_fMaxMZ != m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxMZ ||
                newPlotSettings.PlotSubsetDescription.m_fMinMZ != m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinMZ ||
                newPlotSettings.PlotSubsetDescription.m_fMaxRetentionTime != m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxRetentionTime ||
                newPlotSettings.PlotSubsetDescription.m_fMinRetentionTime != m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinRetentionTime ||
                newPlotSettings.PlotSubsetDescription.m_bGlobalIntensity != m_PlotData.PlotSettings.PlotSubsetDescription.m_bGlobalIntensity ||
                newPlotSettings.PlotSubsetDescription.m_fIntensityTreshold != m_PlotData.PlotSettings.PlotSubsetDescription.m_fIntensityTreshold ||
                newPlotSettings.PlotSubsetDescription.m_iResolutionMZ != m_PlotData.PlotSettings.PlotSubsetDescription.m_iResolutionMZ ||
                newPlotSettings.PlotSubsetDescription.m_iResolutionRetentionTime != m_PlotData.PlotSettings.PlotSubsetDescription.m_iResolutionRetentionTime ||
                newPlotSettings.LogarithmicIntensity != m_PlotData.PlotSettings.LogarithmicIntensity)
            {
                //new subset/filtering, have to rebuild plot
                m_PlotData.PlotSettings = newPlotSettings;
                InternalPlotData newSubset = m_PlotData.GetDataSubset();
                plot3DView.SetData(newSubset);
            }
            else
            {

                m_PlotData.PlotSettings = newPlotSettings;
                plot3DView.UpdatePlot(newPlotSettings);
            }
            plotTypeCombo.SelectedItem = m_PlotData.PlotSettings.PlotType;

        }


        /// <summary>
        /// Returns a PlotSettings structure with settings for current plot. Requires PlotData to be set.
        /// </summary>
        /// <returns>Plot settings</returns>
        public PlotSettings GetCurrentSettings()
        {
            if (m_PlotData == null) throw new NoPlotDataException();    //no plot data, throw an exception


            PlotSettings plotSettings = new PlotSettings();



            plotSettings.LogarithmicIntensity = m_PlotData.PlotSettings.LogarithmicIntensity;
            plotSettings.PlotProjectionType = m_PlotData.PlotSettings.ViewSettings.ProjectionType;
            plotSettings.PlotType = m_PlotData.PlotSettings.PlotType;
            plotSettings.ShowGrid = m_PlotData.PlotSettings.ViewSettings.ShowGrid;
            if (m_PlotData.PlotSettings.PlotSubsetDescription.m_fIntensityTreshold == 100.0f)
                plotSettings.FilteringType = DataFilteringType.NoFiltering;
            else if (m_PlotData.PlotSettings.PlotSubsetDescription.m_bGlobalIntensity)
                plotSettings.FilteringType = DataFilteringType.Plot;
            else
                plotSettings.FilteringType = DataFilteringType.Spectrum;

            plotSettings.FilteringAmount = m_PlotData.PlotSettings.PlotSubsetDescription.m_fIntensityTreshold;
            plotSettings.MaxMZ = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxMZ;
            plotSettings.MinMZ = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinMZ;
            plotSettings.MaxRT = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxRetentionTime;
            plotSettings.MinRT = m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinRetentionTime;
            plotSettings.DisableResampling = m_PlotData.PlotSettings.DisableResampling;
            plotSettings.LockPlotType = m_PlotData.PlotSettings.ViewSettings.LockPlotType;


            return plotSettings;
        }

        /// <summary>
        /// Renders plot to a bitmap.
        /// </summary>
        /// <param name="bitmapWidth">Bitmap width in pixels</param>
        /// <param name="bitmapHeight">Bitmap height in pixels</param>
        /// <param name="printerFriendlyColors">If this parameter is 'true' then
        /// printer-friendly colors are used (white background, black text etc.)</param>
        /// <returns>Bitmap containing the plot</returns>
        public Bitmap RenderPlotToBitmap(int bitmapWidth, int bitmapHeight, bool printerFriendlyColors)
        {
            Bitmap bmp = plot3DView.RenderToBitmap(bitmapWidth, bitmapHeight, printerFriendlyColors);


            return bmp;
        }

       
        #endregion

        /// <summary>
        /// Handles zooming the camera
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void WidgetPlot3D_MouseWheel(object sender, MouseEventArgs e)
        {
            //this.plot3DControl1.Plot3DControl_MouseWheel(sender, e);
            int value = dataZoomScrollBar.Value + (int)(e.Delta / 120);
            if (value >= dataZoomScrollBar.Maximum)
                value = dataZoomScrollBar.Maximum - 1;
            else if (value < 0)
                value = 0;
            dataZoomScrollBar.Value = value;
        }

        /// <summary>
        /// Shows settings dialog
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSettings_Click(object sender, EventArgs e)
        {
            if (m_PlotData != null)
            {
                PlotSettingsForm settingsForm = new PlotSettingsForm();
                //if (settingsForm.ShowDialog(
                //    m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinRetentionTime,
                //    m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxRetentionTime,
                //    m_PlotData.PlotSettings.PlotSubsetDescription.m_fMinMZ,
                //    m_PlotData.PlotSettings.PlotSubsetDescription.m_fMaxMZ,
                //    m_PlotData.PlotSettings.ColorGradient.Colors,
                //    m_PlotData.PlotSettings.PlotSubsetDescription.m_bGlobalIntensity,
                //    m_PlotData.PlotSettings.PlotSubsetDescription.m_fIntensityTreshold,
                //    m_PlotData.PlotSettings.ViewSettings.ShowGrid) == DialogResult.OK)
                //{
                if (settingsForm.ShowDialog(m_PlotData.PlotSettings, m_PlotData.RetentionScansCount, 
                    m_PlotData.MinRetentionTime, m_PlotData.MaxRetentionTime, m_PlotData.MinMZ, m_PlotData.MaxMZ) == DialogResult.OK)
                {
                    PlotSubsetDesc subsetDesc = new PlotSubsetDesc();
                    PlotSubsetDesc currentSubset = m_PlotData.PlotSettings.PlotSubsetDescription;


                    m_PlotData.PlotSettings.ColorGradient.Colors.Clear();
                    m_PlotData.PlotSettings.OverlapLines = settingsForm.GetOverlapLinesPlot();
                    for (int i = 0; i < settingsForm.GetGradientColorsNumber(); i++)
                    {
                        m_PlotData.PlotSettings.ColorGradient.AddColor(settingsForm.GetGradientColor(i));
                    }

                    subsetDesc.m_fMaxMZ = settingsForm.GetMaxMz();
                    subsetDesc.m_fMinMZ = settingsForm.GetMinMz();
                    subsetDesc.m_fMaxRetentionTime = settingsForm.GetMaxRT();
                    subsetDesc.m_fMinRetentionTime = settingsForm.GetMinRT();
                    subsetDesc.m_iResolutionMZ = settingsForm.GetMzResolution();
                    subsetDesc.m_iResolutionRetentionTime = settingsForm.GetRtResolution();
                    bool bShowGrid = settingsForm.ShowGrid;

                    //update view settings
                    ViewSettings currentView = m_PlotData.PlotSettings.ViewSettings;
                    if (bShowGrid != currentView.ShowGrid)
                    {
                        currentView.ShowGrid = bShowGrid;

                    }
                    currentView.ProjectionType = settingsForm.OrthographicProjection ? ProjectionType.Orthographic : ProjectionType.Perspective;
                    currentView.PlotTitle = settingsForm.PlotCaption;
                    currentView.UseSpecular = settingsForm.GetSpecular();
                    currentView.BkgColor = settingsForm.GetBackgroundColor();
                    currentView.PrinterColors = settingsForm.GetPrinterColors();
                    m_PlotData.PlotSettings.ViewSettings = currentView;

                    bool bIsGlobal = false;
                    subsetDesc.m_fIntensityTreshold = settingsForm.GetIntensityalfa(out bIsGlobal);
                    subsetDesc.m_bGlobalIntensity = bIsGlobal;
                    //subsetDesc.m_bisLogIntensity = settingsForm.IsLogarithmicIntensity();
                    //if there was a change in subset then pass new data to Plot3DControl, if no just call UpdatePlot
                    if (subsetDesc.m_fIntensityTreshold != currentSubset.m_fIntensityTreshold ||
                        subsetDesc.m_fMaxMZ != currentSubset.m_fMaxMZ ||
                        subsetDesc.m_fMaxRetentionTime != currentSubset.m_fMaxRetentionTime ||
                        subsetDesc.m_fMinMZ != currentSubset.m_fMinMZ ||
                        subsetDesc.m_fMinRetentionTime != currentSubset.m_fMinRetentionTime ||
                        subsetDesc.m_bGlobalIntensity != currentSubset.m_bGlobalIntensity ||
                        m_PlotData.PlotSettings.LogarithmicIntensity != settingsForm.IsLogarithmicIntensity() ||
                        subsetDesc.m_iResolutionMZ != currentSubset.m_iResolutionMZ ||
                        subsetDesc.m_iResolutionRetentionTime != currentSubset.m_iResolutionRetentionTime)
                    {
                        m_PlotData.PlotSettings.PlotSubsetDescription = subsetDesc;
                        m_PlotData.PlotSettings.LogarithmicIntensity = settingsForm.IsLogarithmicIntensity();
                        //SetDataDelegate newDataDelegate = new SetDataDelegate(plot3DControl1.SetData);
                        plot3DView.IsLoading = true;
                        InternalPlotData plotSubset = m_PlotData.GetDataSubset();
                        //plot3DControl1.BeginInvoke(newDataDelegate, plotSubset);

                        plot3DView.SetData(plotSubset);
                        plotTypeCombo.SelectedItem = m_PlotData.PlotSettings.PlotType;
                        plot3DView.Focus();

                    }
                    else
                    {
                        //if (subsetDesc.m_bOverlapLines != currentSubset.m_bOverlapLines)
                        //    m_PlotData.PlotSettings.PlotSubsetDescription = subsetDesc;
                        plot3DView.UpdatePlot(m_PlotData.PlotSettings);
                    }
                }
            }
            plot3DView.Focus();
        }

        /// <summary>
        /// Resets the view
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReset_Click(object sender, EventArgs e)
        {
            plot3DView.ResetCamera();
            plot3DView.ResetCamera();
            plot3DView.Refresh();
        }

        /// <summary>
        /// Changes plot type
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (m_PlotData == null) return;

            PlotType oldType = m_PlotData.PlotSettings.PlotType;
            PlotType newType = (PlotType)plotTypeCombo.SelectedItem;

            //recompute subset
            plot3DView.IsLoading = true;

            m_PlotData.PlotSettings.PlotType = (PlotType)plotTypeCombo.SelectedItem;
            InternalPlotData subset = m_PlotData.GetDataSubset();
            plot3DView.SetData(subset);

            //if(oldType== PlotType.Points && newType!=oldType)
            //{
            //    //recompute subset
            //    plot3DView.IsLoading = true;

            //    m_PlotData.PlotSettings.PlotType = (PlotType)plotTypeCombo.SelectedItem;
            //    InternalPlotData subset = m_PlotData.GetDataSubset();
            //    plot3DView.SetData(subset);

            //}
            //else if (oldType != PlotType.Points && newType == PlotType.Points)
            //{
            //    //recompute subset
            //    plot3DView.IsLoading = true;

            //    m_PlotData.PlotSettings.PlotType = (PlotType)plotTypeCombo.SelectedItem;
            //    InternalPlotData subset = m_PlotData.GetDataSubset();
            //    plot3DView.SetData(subset);

            //}
            //else
            //{
            //    //UpdatePlotTypeDelegate updateDelegate = new UpdatePlotTypeDelegate(plot3DControl1.UpdatePlot);
            //    //plot3DControl1.BeginInvoke(updateDelegate, newType);
            //    plot3DView.UpdatePlot(newType);
            //    m_PlotData.PlotSettings.PlotType = (PlotType)plotTypeCombo.SelectedItem;
            //    plot3DView.Refresh();
            //}           
        }

        void plotTypeCombo_MouseWheel(object sender, MouseEventArgs e)
        {
            ((HandledMouseEventArgs)e).Handled = true;
        }

        /// <summary>
        /// Updates plot zoom
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void scrlbrZoom_ValueChanged(object sender, EventArgs e)
        {

            if (!m_bSupressZoomEvent)
                plot3DView.SetDataZoom(dataZoomScrollBar.Value);
            //m_iCurrentZoom = dataZoomScrollBar.Value;
        }



        #region Fullscreen

        private bool isFullscreen = false;
        private Control windowedOwner = null;
        private Form fullscreenForm = null;
        private Point windowedLocation;
        private Size windowedSize;
        private Button exitButton;
        private void GoFullscreen()
        {
            isFullscreen = true;
            windowedOwner = plot3DView.Parent;
            windowedLocation = plot3DView.Location;
            windowedSize = plot3DView.Size;
            fullscreenForm = new Form();

            fullscreenForm.WindowState = FormWindowState.Maximized;
            fullscreenForm.FormBorderStyle = FormBorderStyle.None;
            fullscreenForm.SuspendLayout();


            fullscreenForm.Controls.Add(this.plot3DView);
            exitButton = new Button();

            exitButton.AutoSize = true;
            exitButton.FlatStyle = FlatStyle.Flat;
            


            exitButton.Text = "Exit Fullscreen";
            exitButton.Click += new EventHandler(exitButton_Click);
            fullscreenForm.Controls.Add(exitButton);
            exitButton.BringToFront();
            fullscreenForm.ResumeLayout(true);
            fullscreenForm.Load += new EventHandler(fullscreenForm_Load);
            fullscreenForm.MouseWheel+=new MouseEventHandler(WidgetPlot3D_MouseWheel);
            fullscreenForm.Show();
        }

        void exitButton_Click(object sender, EventArgs e)
        {
            GoWindowed();
        }

        void fullscreenForm_Load(object sender, EventArgs e)
        {
            plot3DView.Size = fullscreenForm.ClientSize;
            plot3DView.Location = new Point(0, 0);
            exitButton.Location = new Point(fullscreenForm.ClientSize.Width - exitButton.Size.Width - 5, 10);

        }
        private void GoWindowed()
        {
            isFullscreen = false;

            plot3DView.Size = windowedSize;
            plot3DView.Location = windowedLocation;
            windowedOwner.Controls.Add(plot3DView);

            fullscreenForm.Close();

            Refresh();
        }
        #endregion
        private void cameraZoomScrollBar_ValueChanged(object sender, EventArgs e)
        {

            plot3DView.SetCameraZoom(cameraZoomScrollBar.Value);
        }

        private void btsSaveScreen_Click(object sender, EventArgs e)
        {
            SaveScreenshot();
        }

        internal void SaveScreenshot()
        {
            SaveFileSettingsForm saveForm = new SaveFileSettingsForm();
            if (saveForm.ShowDialog() == DialogResult.OK)
            {
                if (saveFileDialogPic.ShowDialog() == DialogResult.OK)
                {
                    plot3DView.RenderToBitmap(saveForm.GetWidth(), saveForm.GetHeight(),
                        saveForm.GetPrinterColors()).Save(saveFileDialogPic.FileName, System.Drawing.Imaging.ImageFormat.Png);
                }
            }
        }

        private void WidgetPlot3D_MouseUp(object sender, MouseEventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (button1.Text == "Select")
            {
                button1.Text = "Rotate";
                plot3DView.SelectionMode = false;
            }
            else
            {
                button1.Text = "Select";
                plot3DView.SelectionMode = true;
            }
        }

        bool m_bSupressZoomEvent = false;
        /// <summary>
        /// Sets correct zoom on the slider
        /// </summary>
        /// <param name="zoom">Data zoom: 0-no zoom, 100-maximum zoom</param>
        internal void SetZoom(int zoom)
        {
            if (zoom < dataZoomScrollBar.Minimum || zoom >= dataZoomScrollBar.Maximum) return; //invalid parameter

            m_bSupressZoomEvent = true;
            this.dataZoomScrollBar.Value = zoom;
            m_bSupressZoomEvent = false;
        }

        private void btnFullScreen_Click(object sender, EventArgs e)
        {

            GoFullscreen();
        }



    }

    /// <summary>
    /// Specified plot settings contained some invalid parameters.
    /// </summary>
    public class InvalidPlotSettingsException : Exception { }

    /// <summary>
    /// This operation requires plot data to be present.
    /// </summary>
    public class NoPlotDataException : Exception { }
}
